home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Graphics
/
nxyplot
/
Source
/
PlotView.m
< prev
next >
Wrap
Text File
|
1994-02-01
|
56KB
|
1,728 lines
/* Generated by Interface Builder */
#import "defs.h"
#import "PlotView.h"
#import "Plot.h"
#import <appkit/SavePanel.h>
#import <appkit/color.h>
#import <appkit/FormCell.h>
#import <appkit/PrintPanel.h>
#import <appkit/Pasteboard.h>
/* The following routines are in auxil.m: */
extern void count_labels(int *, double *, double, double, double);
extern void autoformat(double, double, double, int *);
extern void handformat(float, char *, int *);
@implementation PlotView
- (BOOL) acceptsFirstMouse { return YES;} /* grab that mouse down event! */
- provideMainTitleFont { return newMainTitleFont;}
- provideXTitleFont { return newXTitleFont;}
- provideYTitleFont { return newYTitleFont;}
- provideLegendTitleFont { return newLegendTitleFont;}
- provideLegendFont { return newLegendFont;}
- provideTicLabelFont { return newTicLabelFont;}
- (NXPoint) provideLegendBoxOrigin { return legendbox.origin;}
- (NXPoint) provideXTitleBoxOrigin { return xtitlebox.origin;}
- (NXPoint) provideYTitleBoxOrigin { return ytitlebox.origin;}
- (NXPoint) provideMainTitleBoxOrigin { return maintitlebox.origin;}
- provideWindowFrame:(NXRect *)windowframe
{
return [[self window] getFrame:windowframe];
}
/* All these method names that start with "force" would more naturally start
* with "set", but everything malfunctions then; apparently it's a bad idea
* to have methods whose names begin with "set". Is it also a bad idea to
* have methods whose names begin with "init"?
*/
- forceWindowFrame:(NXRect *)windowframe
{
[[self window] placeWindow:windowframe];
oldbounds = bounds; /* have to do this right here so that the */
[self display]; /* title boxes are not moved around again */
return self;
}
- forceLegendBoxOrigin: (NXPoint)point
{
legendbox.origin = point;
return self;
}
- forceXTitleBoxOrigin: (NXPoint)point
{
xtitlebox.origin = point;
return self;
}
- forceYTitleBoxOrigin: (NXPoint)point
{
ytitlebox.origin = point;
return self;
}
- forceMainTitleBoxOrigin: (NXPoint)point
{
maintitlebox.origin = point;
return self;
}
- forceMainTitleFont:(char *)fontname :(float)fontsize
{
newMainTitleFont =
[Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
return self;
}
- forceXTitleFont:(char *)fontname :(float)fontsize;
{
newXTitleFont =
[Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
return self;
}
- forceYTitleFont:(char *)fontname :(float)fontsize;
{
newYTitleFont =
[Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
return self;
}
- forceLegendFont:(char *)fontname :(float)fontsize;
{
newLegendFont =
[Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
return self;
}
- forceLegendTitleFont:(char *)fontname :(float)fontsize;
{
newLegendTitleFont =
[Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
return self;
}
- forceTicLabelFont:(char *)fontname :(float)fontsize;
{
newTicLabelFont =
[Font newFont:fontname size:fontsize style:0 matrix:NX_IDENTITYMATRIX];
return self;
}
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
legendbox.origin.x = XOFFSET + 50.0; /* get the legendbox initialized here */
legendbox.origin.y = YOFFSET + 50.0;
xtitlebox.origin.x = XOFFSET/4.0
+ (bounds.size.width - DEFAULTAXISTITLEWIDTH)/2.0;
xtitlebox.origin.y = bounds.origin.y + 16.0;
xtitlebox.size.width = DEFAULTAXISTITLEWIDTH;
xtitlebox.size.height = DEFAULTFONTSIZE;
ytitlebox.origin.x = bounds.origin.x + 36.0;
ytitlebox.origin.y = YOFFSET/4.0
+ (bounds.size.height - DEFAULTAXISTITLEWIDTH)/2.0;
ytitlebox.size.width = DEFAULTFONTSIZE;
ytitlebox.size.height = DEFAULTAXISTITLEWIDTH;
maintitlebox.origin.x = XOFFSET/4.0
+ (bounds.size.width - DEFAULTMAINTITLEWIDTH)/2.0;
maintitlebox.origin.y = bounds.size.height - 24.0;
maintitlebox.size.width = DEFAULTMAINTITLEWIDTH;
maintitlebox.size.height = 14.0;
/*
* With NS 3.0, we start handling the dragging code here (not in PlotDelegate).
* registerForDraggedTypes is a new method in the View class.
*/
[self registerForDraggedTypes:&NXFilenamePboardType count:1];
oldbounds = bounds;
return self;
}
- (NXCoord *)xdata:(int)n /* we use this in drawing the legend curves */
{
xlegend[0] = (legendbox.origin.x + 5.33333)/ppxunit;
/* We used to have 5.0 instead of 5.33333, but that can give erroneous-looking
* (albeit correct) results when writing to the screen: the pixels that are
* turned on when drawing a filled circle, for example, are much different
* when the circle's center is precisely at a pixel than when the circle's center
* is not precisely at a pixel. The results are much better when the center
* of the circle is at a half-pixel point, and so that's what we do.
*/
xlegend[1] = xlegend[0] + 40.0/ppxunit;
/* curve fragment in legend is 40 pixels in length */
if (!drawingLegendLines) xlegend[0] = 0.5*(xlegend[0] + xlegend[1]);
return xlegend;
}
- (NXCoord **)ydata:(int)n /* we use this in drawing the legend curves */
{
int ncurves = [plotParam nCurvesTotal];
int j;
float yloc, yhgt;
const char * curvetitle;
if (newLegendFont) {
yhgt = [newLegendFont pointSize];
}
else {
yhgt = DEFAULTFONTSIZE;
}
yloc = (legendbox.origin.y - 2.0)/ppyunit;
ylegend = (NXCoord **)realloc((void *)ylegend, ncurves*sizeof(NXCoord *));
for (j = 0; j < ncurves; j++) {
*(ylegend+j) = (NXCoord *)NULL; /* this is necessary */
*(ylegend+j) = (NXCoord *)realloc((void *)*(ylegend+j), 2*sizeof(NXCoord));
/* check for no lines and no symbols */
if ( ([plotParam providelinestyle:j] == NOLINE) &&
([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue;
/* check for empty curvetitle */
curvetitle = [legendForm stringValueAt:j];
if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0')
continue;
yloc = yloc + (yhgt+0.33333)/ppyunit;
/* We used to have just yhgt (instead of yhgt+0.33333); see comments in preceding
* routine for the reason to change it.
*/
}
for (j=0; j<ncurves; j++) {
*(*(ylegend+j)+0) = yloc;
*(*(ylegend+j)+1) = yloc;
/* check for no lines and no symbols */
if( ([plotParam providelinestyle:j] == NOLINE) &&
( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue;
/* check for empty curvetitle */
curvetitle = [legendForm stringValueAt:j];
if ((drawingLegendLines || drawingLegendSymbols) && curvetitle[0]=='\0')
continue;
yloc -= yhgt/ppyunit;
}
return ylegend;
}
- (BOOL)has_ebars:(int)n
{
return NO;
}
- (int)nPoints:(int)n
{
if (drawingLegendLines) return 2;
else return 1;
}
- (int)nCurves:(int)n
{
return [plotParam nCurvesTotal];
}
- (int)nFiles
{
return 1;
}
- startPlot
{
const char * xtitle = [titles stringValueAt:0];
const char * ytitle = [titles stringValueAt:1];
const char * maintitle = [titles stringValueAt:2];
float yhgt;
id mainTitleFont,axisTitleFont;
NXSetColor([plotParam provideBackgroundColor]);
[self clear:self];
PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
[accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
LINE_WIDTH_IF_PRINTING_COLOR);
NXSetColor([plotParam provideTextColor]);
/* get new font from fontmanager */
if ([plotParam shouldChangeXTitleFont]) {
newXTitleFont = [theFontManager convertFont:[theFontManager selFont]];
}
if (newXTitleFont) {
axisTitleFont = [Font newFont:[newXTitleFont name]
size:[newXTitleFont pointSize]
style:[newXTitleFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newXTitleFont pointSize];
}
else {
axisTitleFont =
[Font newFont:"Helvetica" size:DEFAULTFONTSIZE
style:0 matrix:NX_IDENTITYMATRIX];
yhgt = DEFAULTFONTSIZE;
}
[axisTitleFont set];
xtitlebox.size.width = [axisTitleFont getWidthOf:xtitle];
xtitlebox.size.height = yhgt;
if (oldbounds.size.width != bounds.size.width
|| oldbounds.size.height != bounds.size.height) {
/* Couldn't figure out how to do this with windowDidResize or superviewSizeChanged,
* so this grubby idea (with the global oldbounds variable) is used instead.
*/
xtitlebox.origin.x = (xtitlebox.origin.x + 0.5*xtitlebox.size.width)
*(bounds.size.width/oldbounds.size.width) - 0.5*xtitlebox.size.width;
// xtitlebox.origin.y = (xtitlebox.origin.y + 0.5*xtitlebox.size.height)
// *(bounds.size.height/oldbounds.size.height) - 0.5*xtitlebox.size.height;
legendbox.origin.x = (legendbox.origin.x + 0.5*legendbox.size.width)
*(bounds.size.width/oldbounds.size.width) - 0.5*legendbox.size.width;
legendbox.origin.y = (legendbox.origin.y + 0.5*legendbox.size.height)
*(bounds.size.height/oldbounds.size.height) - 0.5*legendbox.size.height;
}
PSmoveto(xtitlebox.origin.x, xtitlebox.origin.y);
PSshow((char *)xtitle);
/* get new font from fontmanager */
if ([plotParam shouldChangeYTitleFont]) {
newYTitleFont =
[theFontManager convertFont:[theFontManager selFont]];
}
if (newYTitleFont) {
axisTitleFont = [Font newFont:[newYTitleFont name]
size:[newYTitleFont pointSize]
style:[newYTitleFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newYTitleFont pointSize];
}
else {
axisTitleFont =
[Font newFont:"Helvetica" size:DEFAULTFONTSIZE
style:0 matrix:NX_IDENTITYMATRIX];
yhgt = DEFAULTFONTSIZE;
}
[axisTitleFont set];
ytitlebox.size.width = yhgt;
ytitlebox.size.height = [axisTitleFont getWidthOf:ytitle];
if (oldbounds.size.width != bounds.size.width
|| oldbounds.size.height != bounds.size.height) {
// ytitlebox.origin.x = (ytitlebox.origin.x + 0.5*ytitlebox.size.height)
// *(bounds.size.width/oldbounds.size.width) - 0.5*ytitlebox.size.height;
ytitlebox.origin.y = (ytitlebox.origin.y + 0.5*ytitlebox.size.width)
*(bounds.size.height/oldbounds.size.height) - 0.5*ytitlebox.size.width;
}
PSmoveto(ytitlebox.origin.x + yhgt, ytitlebox.origin.y);
PSgsave();
PSrotate(90.0);
PSshow((char *)ytitle);
PSgrestore();
if ([plotParam shouldChangeMainTitleFont]) {
newMainTitleFont =
[theFontManager convertFont:[theFontManager selFont]];
}
if (newMainTitleFont) {
mainTitleFont = [Font newFont:[newMainTitleFont name]
size:[newMainTitleFont pointSize]
style:[newMainTitleFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newMainTitleFont pointSize];
}
else {
mainTitleFont =
[Font newFont:"Helvetica" size:14.0 style:0 matrix:NX_IDENTITYMATRIX];
yhgt = 14.0;
}
[mainTitleFont set];
maintitlebox.size.width = [mainTitleFont getWidthOf:maintitle];
maintitlebox.size.height = yhgt;
if (oldbounds.size.width != bounds.size.width
|| oldbounds.size.height != bounds.size.height) {
maintitlebox.origin.x = (maintitlebox.origin.x + 0.5*maintitlebox.size.width)
*(bounds.size.width/oldbounds.size.width) - 0.5*maintitlebox.size.width;
maintitlebox.origin.y = (maintitlebox.origin.y + 0.5*maintitlebox.size.height)
*(bounds.size.height/oldbounds.size.height) - 0.5*maintitlebox.size.height;
oldbounds = bounds;
}
PSmoveto(maintitlebox.origin.x, maintitlebox.origin.y);
PSshow((char *)maintitle);
// box around the plot:
if ([borderBoxOnOff state]) {
PSsetlinewidth([borderBoxThicknessText floatValue]);
PSmoveto(0.0, 0.0);
PSlineto(bounds.size.width, 0.0);
PSlineto(bounds.size.width, bounds.size.height);
PSlineto(0.0, bounds.size.height);
PSlineto(0.0, 0.0);
PSstroke();
PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
[accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
LINE_WIDTH_IF_PRINTING_COLOR);
}
// The preceding doesn't work quite right: for some reason the vertical line
// on the right hand side of the plot doesn't show up on the display. The
// line is there if you print or preview the file, however.
return self;
}
- setDrawColor:(float) color
{
PSsetgray(color);
return self;
}
- drawLines:sender :(BOOL)xaxislog :(BOOL)yaxislog
/*
* This is coded so that when drawLines is called with plotParam as argument,
* it draws the data curves; when drawLines is called with self (plotView) as
* argument, it draws the short line segments in the legend box.
*/
{
int i, j, jrun, n;
NXCoord *x;
NXCoord **y;
int npoints, ncurves;
int curveindex = 0; /* cumulative index */
int linestyle;
float thick = [lineThicknessText floatValue];
float pattern0[] = {}; /* solid */
float pattern1[] = {4.0, 4.0}; /* dash */
float pattern2[] = {1.0, 3.0}; /* dot */
float pattern3[] = {7.0, 3.0, 3.0, 3.0}; /* chain dash */
float pattern4[] = {7.0, 4.0, 1.0, 4.0}; /* chain dot */
const char * curvetitle;
for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */
x = [sender xdata:n];
y = [sender ydata:n];
ncurves = [sender nCurves:n];
npoints = [sender nPoints:n];
PSnewpath();
PSsetlinewidth(NXDrawingStatus==NX_DRAWING? thick :
[accPrintColorButton state]==0? MAX(thick, LINE_WIDTH_IF_PRINTING_BW) :
MAX(thick, LINE_WIDTH_IF_PRINTING_COLOR));
for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
linestyle = [plotParam providelinestyle:jrun];
NXSetColor([plotParam provideCurveColor:jrun]);
switch(linestyle) {
case SOLID:
PSsetdash(pattern0, 0, 0.0);
break;
case DASH:
PSsetdash(pattern1, 2, 0.0);
break;
case DOT:
PSsetdash(pattern2, 2, 0.0);
break;
case CHAINDASH:
PSsetdash(pattern3, 4, 0.0);
break;
case CHAINDOT:
PSsetdash(pattern4, 4, 0.0);
break;
case NOLINE: /* no lines */
continue;
}
if (linestyle == NOLINE) continue; /* no lines, go to next curve */
/* If we're drawing the legend and there is no title, don't
* bother to draw the line:
*/
curvetitle = [legendForm stringValueAt:jrun];
if (drawingLegendLines && curvetitle[0]=='\0') continue;
j = jrun - curveindex; /* for indexing into the y array */
if (!xaxislog && !yaxislog) {
PSmoveto(x[0]*ppxunit, *(*(y + j)) * ppyunit );
for (i=1; i<npoints; i++) {
if ( (i % 512) == 0 ) {
PSstroke();
PSnewpath();
PSmoveto(x[i-1]*ppxunit, *(*(y+j)+i-1) * ppyunit);
}
PSlineto(x[i]*ppxunit, *(*(y+j)+i) * ppyunit);
}
PSstroke();
}
else if (!xaxislog && yaxislog) {
PSmoveto(x[0]*ppxunit, (float)log10((double)*(*(y + j))) * ppyunit );
for (i=1; i<npoints; i++) {
if ( (i % 512) == 0 ) {
PSstroke();
PSnewpath();
PSmoveto(x[i-1]*ppxunit,
(float)log10((double)*(*(y+j)+i-1)) * ppyunit);
}
PSlineto(x[i]*ppxunit, (float)log10((double)*(*(y+j)+i)) * ppyunit);
}
PSstroke();
}
else if (xaxislog && !yaxislog) {
PSmoveto((float)log10((double)x[0])*ppxunit, *(*(y + j)) * ppyunit );
for (i=1; i<npoints; i++) {
if ( (i % 512) == 0 ) {
PSstroke();
PSnewpath();
PSmoveto((float)log10((double)x[i-1])*ppxunit,
*(*(y+j)+i-1) * ppyunit);
}
PSlineto((float)log10((double)x[i])*ppxunit, *(*(y+j)+i) * ppyunit);
}
PSstroke();
}
else if (xaxislog && yaxislog) {
PSmoveto((float)log10((double)x[0])*ppxunit,
(float)log10((double)*(*(y + j))) * ppyunit );
for (i=1; i<npoints; i++) {
if ( (i % 512) == 0 ) {
PSstroke();
PSnewpath();
PSmoveto((float)log10((double)x[i-1])*ppxunit,
(float)log10((double)*(*(y+j)+i-1)) * ppyunit);
}
PSlineto((float)log10((double)x[i])*ppxunit,
(float)log10((double)*(*(y+j)+i)) * ppyunit);
}
PSstroke();
}
}
curveindex += ncurves;
}
PSsetdash(pattern0, 0, 0.0); /* back to solid lines */
return self;
}
/*
* Our first idea for drawing the symbols was: draw each symbol just once
* in an off-screen window, then composite them in where needed. This worked
* fine for screen drawing (modulo a little difficulty in compositing to just
* the right spot), but was a miserable failure when it came to printing: the
* print machinery scaled the composite bitmap. We finally decided to abandon
* compositing, in the interest of keeping our screen drawing code the same as
* our printing code. Should check this again under version 2.0.
*/
- drawSymbols:sender :(BOOL)xaxislog :(BOOL)yaxislog
/*
* This is coded so that when drawSymbols is called with plotParam as argument,
* it draws the data curves; when drawSymbols is called with self (plotView) as
* argument, it draws the symbols in the legend box.
*/
{
/* Our current code in drawSymbols could perhaps be sped up by using wraps
* or DPSuserpath(). See if it's too slow as it stands before trying to
* speed it up.
*/
int i, j, jrun, n;
float size = 4.0;
NXCoord *x;
NXCoord **y;
int npoints, ncurves;
int curveindex = 0;
int symbolstyle;
register float xtmp, ytmp;
const char * curvetitle;
size = [symbolSizeText floatValue];
PSgsave();
for (n=0; n<[sender nFiles]; n++) { /* loop over all active files */
x = [sender xdata:n];
y = [sender ydata:n];
ncurves = [sender nCurves:n];
npoints = [sender nPoints:n];
PSnewpath();
PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
[accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
LINE_WIDTH_IF_PRINTING_COLOR);
for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
symbolstyle = [plotParam providesymbolstyle:jrun];
NXSetColor([plotParam provideCurveColor:jrun]);
/* If we're drawing the legend and there is no title, don't
* bother to draw the symbol:
*/
curvetitle = [legendForm stringValueAt:jrun];
if (drawingLegendSymbols && curvetitle[0]=='\0') continue;
j = jrun - curveindex; /* for indexing into the y array */
switch(symbolstyle) {
case NOSYMBOL:
continue;
break;
case CIRCLE:
for (i=0; i<npoints; i++) {
/* circle_at(x[i], *(*(y+j)+i)); */
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
PSarc(xtmp * ppxunit, ytmp * ppyunit, size, 0.0, 360.0);
PSfill();
}
break;
case XMARK:
for (i=0; i<npoints; i++) {
/* x_at(x[i], *(*(y+j)+i)); */
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit - size);
PSrlineto(2.0*size, 2.0*size);
PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit + size);
PSrlineto(2.0*size, -2.0*size);
PSstroke();
}
break;
case UPTRIANGLE:
for (i=0; i<npoints; i++) {
/* uptriangle_at(x[i], *(*(y+j)+i)); */
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit - size/SQRT3);
PSrlineto(2.0*size, 0.0);
PSrlineto(-size, size*SQRT3);
PSclosepath();
PSfill();
}
break;
case DOWNTRIANGLE:
for (i=0; i<npoints; i++) {
/* downtriangle_at(x[i], *(*(y+j)+i)); */
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit + size/SQRT3);
PSrlineto(2.0*size, 0.0);
PSrlineto(-size, -size*SQRT3);
PSclosepath();
PSfill();
}
break;
case DIAMOND:
for (i=0; i<npoints; i++) {
/* diamond_at(x[i], *(*(y+j)+i)); */
/* 3 pixels horizontal, 4 pixels vertical */
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
PSmoveto(xtmp * ppxunit, ytmp * ppyunit - size);
PSrlineto(3.0/4.0*size, size);
PSrlineto(-3.0/4.0*size, size); /* gives a pleasing diamond shape */
PSrlineto(-3.0/4.0*size, -size);
PSclosepath();
PSfill();
}
break;
case SQUARE:
for (i=0; i<npoints; i++) {
/* square_at(x[i], *(*(y+j)+i)); */
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
PSrectfill(xtmp * ppxunit - size, ytmp * ppyunit - size,
2.0*size, 2.0*size);
}
break;
case PLUS:
for (i=0; i<npoints; i++) {
/* plus_at(x[i], *(*(y+j)+i)); */
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
PSmoveto(xtmp * ppxunit, ytmp * ppyunit - size);
PSrlineto(0.0, 2.0*size);
PSmoveto(xtmp * ppxunit - size, ytmp * ppyunit);
PSrlineto(2.0*size, 0.0);
PSstroke();
}
break;
}
}
curveindex += ncurves;
}
PSgrestore();
return self;
}
- drawErrorBars :(BOOL)xaxislog :(BOOL)yaxislog
{
int n, j, npoints, ncurves, jrun, i;
NXCoord *x, *ex = NULL; /* ex and ey initialized to avoid */
NXCoord **y, **ey = NULL; /* compiler warning */
int curveindex = 0;
register float xtmp, ytmp, tmp1, tmp2;
float width = [errorBarBaseWidth floatValue];
BOOL exbars, eybars;
PSgsave();
for (n=0; n<[plotParam nFiles]; n++) { /* loop over all active files */
exbars = [plotParam has_exbars:n];
eybars = [plotParam has_eybars:n];
ncurves = [plotParam nCurves:n];
if (!exbars && !eybars ) { /* skip if no error bars */
curveindex += ncurves; /* but have to bump this index, */
continue; /* to get the colors correct */
}
x = [plotParam xdata:n]; /* we have to draw error bars */
if (exbars) {
ex = [plotParam exdata:n];
}
y = [plotParam ydata:n];
if (eybars) {
ey = [plotParam eydata:n];
}
npoints = [plotParam nPoints:n];
PSnewpath();
PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
[accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
LINE_WIDTH_IF_PRINTING_COLOR);
for (jrun=curveindex; jrun<curveindex+ncurves; jrun++) {
NXSetColor([plotParam provideCurveColor:jrun]);
j = jrun - curveindex; /* for indexing into the y and ey arrays */
/* check if error bars are desired */
if (exbars && [[errorBarMatrix cellAt :n :0] state] == 1) {
for (i=0; i<npoints; i++) {
ytmp = (yaxislog? (float)log10((double)*(*(y+j)+i)) : *(*(y+j)+i));
if (xaxislog) {
if (x[i] - ex[i] <= 0.0) { /* avoid log of negative */
tmp1 = (float)log10( (double) x[i] ); /* what to do? */
}
else {
tmp1 = (float)log10( (double) (x[i] - ex[i]) );
}
if (x[i] + ex[i] <= 0.0) { /* again avoid log of negative */
tmp2 = (float)log10( (double) x[i] );
}
else {
tmp2 = (float)log10( (double) (x[i] + ex[i]) );
}
}
else {
tmp1 = x[i] - ex[i];
tmp2 = x[i] + ex[i];
}
PSmoveto(tmp1 * ppxunit, ytmp * ppyunit);
PSrlineto((tmp2-tmp1)*ppxunit, 0.0);
if (width > 0.0) {
PSmoveto(tmp1 * ppxunit, ytmp*ppyunit - width/2.0);
PSrlineto(0.0, width);
PSmoveto(tmp2 * ppxunit, ytmp*ppyunit - width/2.0);
PSrlineto(0.0, width);
}
PSstroke();
}
}
if (eybars && [[errorBarMatrix cellAt :n :j+1] state] == 1) {
for (i=0; i<npoints; i++) {
xtmp = (xaxislog? (float)log10((double)x[i]) : x[i]);
if (yaxislog) {
if ((*(*(y+j)+i) - *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */
tmp1 = (float)log10( (double) (*(*(y+j)+i)) );
}
else {
tmp1 = (float)log10( (double) (*(*(y+j)+i) - *(*(ey+j)+i)) );
}
if ((*(*(y+j)+i) + *(*(ey+j)+i)) <= 0.0) { /* avoid log of negative */
tmp2 = (float)log10( (double) (*(*(y+j)+i)) );
}
else {
tmp2 = (float)log10( (double) (*(*(y+j)+i) + *(*(ey+j)+i)) );
}
}
else {
tmp1 = *(*(y+j)+i) - *(*(ey+j)+i);
tmp2 = *(*(y+j)+i) + *(*(ey+j)+i);
}
PSmoveto(xtmp * ppxunit, tmp1 * ppyunit);
PSrlineto(0.0, (tmp2-tmp1)*ppyunit);
if (width > 0.0) {
PSmoveto(xtmp*ppxunit - width/2.0, tmp1 * ppyunit);
PSrlineto(width, 0.0);
PSmoveto(xtmp*ppxunit - width/2.0, tmp2 * ppyunit);
PSrlineto(width, 0.0);
}
PSstroke();
}
}
}
curveindex += ncurves;
}
PSgrestore();
return self;
}
- startLegend
{
id legendtextfont,legendTitleFont;
const char * curvetitle;
const char * legendtitle = [legendTitle stringValueAt:0];
int j;
int ncurves = [plotParam nCurvesTotal];
float maxcurvetitlewid = 0;
float yhgt;
if ([plotParam shouldChangeLegendFont]) {
newLegendFont = [theFontManager convertFont:[theFontManager selFont]];
}
if (newLegendFont) {
legendtextfont = [Font newFont:[newLegendFont name]
size:[newLegendFont pointSize]
style:[newLegendFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newLegendFont pointSize]; /* height of text */
}
else {
legendtextfont =
[Font newFont:"Helvetica" size:DEFAULTFONTSIZE
style:0 matrix:NX_IDENTITYMATRIX];
yhgt = DEFAULTFONTSIZE;
}
[legendtextfont set];
legendbox.size.height = 10.0;
for (j=0; j<ncurves; j++) {
/* check for no lines and no symbols */
if( ([plotParam providelinestyle:j] == NOLINE) &&
( [plotParam providesymbolstyle:j] == NOSYMBOL) )continue;
curvetitle = [legendForm stringValueAt:j];
/* skip this curve if there is no title: */
if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue;
maxcurvetitlewid = MAX(maxcurvetitlewid,
[legendtextfont getWidthOf:curvetitle]);
legendbox.size.height += yhgt; /* increment by yhgt for every curve */
}
/* check on legend title width also */
if ([plotParam shouldChangeLegendTitleFont]) {
newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]];
}
if (newLegendTitleFont) {
legendTitleFont = [Font newFont:[newLegendTitleFont name]
size:[newLegendTitleFont pointSize]
style:[newLegendTitleFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newLegendTitleFont pointSize]; /* height of text */
}
else {
legendTitleFont =
[Font newFont:"Helvetica" size:DEFAULTFONTSIZE
style:0 matrix:NX_IDENTITYMATRIX];
yhgt = DEFAULTFONTSIZE;
}
[legendTitleFont set];
maxcurvetitlewid = MAX(maxcurvetitlewid,
[legendTitleFont getWidthOf:legendtitle]);
legendbox.size.width = 5.0 + 40.0 + 5.0 + maxcurvetitlewid + 5.0;
/* legendboxwidth = L. margin + 40 + space + curve title + R. margin */
if([legendTitleFont getWidthOf:legendtitle] != 0)
legendbox.size.height = legendbox.size.height + 2.0*yhgt - 4.0;
return self;
}
- drawLegend:sender
{
id legendtextfont,legendTitleFont;
const char * curvetitle;
const char * legendtitle = [legendTitle stringValueAt:0];
int j;
int ncurves = [plotParam nCurvesTotal];
float yhgt;
if ([plotParam shouldChangeLegendFont]) {
newLegendFont = [theFontManager convertFont:[theFontManager selFont]];
}
/* get legend text font initialized */
if (newLegendFont) {
legendtextfont = [Font newFont:[newLegendFont name]
size:[newLegendFont pointSize]
style:[newLegendFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newLegendFont pointSize];
}
else {
legendtextfont =
[Font newFont:"Helvetica" size:DEFAULTFONTSIZE
style:0 matrix:NX_IDENTITYMATRIX];
yhgt = DEFAULTFONTSIZE;
}
[legendtextfont set];
drawingLegendLines = YES;
[self xdata:0];
[self ydata:0];
NXSetColor([plotParam provideBackgroundColor]);
if ([legendOpaque state] == 0) {
NXRectFill(&legendbox);
}
[sender drawLines:sender :NO :NO]; /* legend lines, linear axes always */
NXSetColor([plotParam provideTextColor]);
for (j=0; j<ncurves; j++) {
/* check for no lines and no symbols */
if ( ([plotParam providelinestyle:j] == NOLINE) &&
([plotParam providesymbolstyle:j] == NOSYMBOL) ) continue;
curvetitle = [legendForm stringValueAt:j];
/* skip this curve if there is no title: */
if ([legendtextfont getWidthOf:curvetitle] == 0.0) continue;
PSmoveto(xlegend[1]*ppxunit + 5.0,
*(*(ylegend+j)+0) * ppyunit - 0.33*yhgt);
PSshow((char *)curvetitle);
}
if ([plotParam shouldChangeLegendTitleFont]) {
newLegendTitleFont = [theFontManager convertFont:[theFontManager selFont]];
}
if (newLegendTitleFont) {
legendTitleFont = [Font newFont:[newLegendTitleFont name]
size:[newLegendTitleFont pointSize]
style:[newLegendTitleFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newLegendTitleFont pointSize]; /* height of text */
}
else {
legendTitleFont =
[Font newFont:"Helvetica" size:DEFAULTFONTSIZE
style:0 matrix:NX_IDENTITYMATRIX];
yhgt = DEFAULTFONTSIZE;
}
[legendTitleFont set];
PSmoveto(xlegend[0]*ppxunit - 5.0 + legendbox.size.width/2.0
- 0.5*[legendTitleFont getWidthOf:legendtitle],
*(*(ylegend+0)+0) * ppyunit + yhgt);
PSshow((char *)legendtitle);
if([legendBoxOnOff state]) {
PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
[accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
LINE_WIDTH_IF_PRINTING_COLOR);
PSrectstroke(legendbox.origin.x, legendbox.origin.y,
legendbox.size.width, legendbox.size.height);
}
drawingLegendLines = NO;
drawingLegendSymbols = YES;
[sender drawSymbols:sender :NO :NO]; /* legend symbols, linear axes always */
drawingLegendSymbols = NO;
return self;
}
- clear:sender
{
/*
* Derek Lisoski (dlisoski@cco.caltech.edu) suggests not drawing the
* opaque background rectangle when you print or save a file. Then
* when you read the saved plots into a separate drawing program you
* can overlay multiple plots or get the plots arbitrarily close to
* each other. There may be some problems with background colors
* when importing into various applications (Create or Draw, e.g.)
*/
if (NXDrawingStatus == NX_DRAWING || [opaqueBackgroundButton state] == 0) {
NXRectFill(&bounds);
/* for color; had NXEraseRect, but that always fills with white */
}
return self;
}
/*
* This routine assumes it is called with xmin != xmax and ymin != ymax.
* Bad things may happen if this is not the case.
* The input parameters are assumed to be in pixel coordinates.
*/
- drawTicMarks:(float)xmin :(float)xmax :(float)ymin :(float)ymax
{
float pattern0[] = {}; /* solid */
float pattern2[] = {1.0, 3.0}; /* dot */
double xinc_unscaled = [plotParam provideXinc];
double yinc_unscaled = [plotParam provideYinc];
double xmin_unscaled = [plotParam provideXmin];
double ymin_unscaled = [plotParam provideYmin];
double xmax_unscaled = [plotParam provideXmax];
double ymax_unscaled = [plotParam provideYmax];
/* It is useful in some fairly extreme cases to have the increments
* not in pixel coordinates (otherwise can get "bad" labels).
*/
char ticlabel[32];
id ticfont, ticfont1;
float x, y, ticloc_rat, xticloc, yticloc;
BOOL drawGrid = [gridOnOff state];
BOOL xaxislog = [plotParam xaxisLog];
BOOL yaxislog = [plotParam yaxisLog];
BOOL drawMinorTics = [minorTicMarksOnOff state];
int ticLocation; /* 0=axes, 1=2 sides, 2=4 sides */
float ticmarklen = [ticMarkLengthText floatValue];
int nmin, ninc, nmax, j, i;
float ticloc, xwid, yhgt, yhgt1;
double first;
int nlabels;
int axformat[3];
if (strncmp([ticMarkLocation title], "Axes", 4) == 0)
ticLocation = 0;
else if (strncmp([ticMarkLocation title], "Frame (2 sides)", 15) == 0)
ticLocation = 1;
else
ticLocation = 2;
if ([plotParam shouldChangeTicLabelFont]) {
newTicLabelFont = [theFontManager convertFont:[theFontManager selFont]];
}
if (newTicLabelFont) {
ticfont = [Font newFont:[newTicLabelFont name]
size:[newTicLabelFont pointSize]
style:[newTicLabelFont style]
matrix:NX_IDENTITYMATRIX];
yhgt = [newTicLabelFont pointSize];
yhgt1 = (yhgt >= 10.0? yhgt - 2.0 : 8.0);
ticfont1 = [Font newFont:[newTicLabelFont name]
size:yhgt1
style:[newTicLabelFont style]
matrix:NX_IDENTITYMATRIX];
}
else {
ticfont = [Font newFont:"Helvetica" size:DEFAULTFONTSIZE
style:0 matrix:NX_IDENTITYMATRIX];
ticfont1 =
[Font newFont:"Helvetica" size:10.0 style:0 matrix:NX_IDENTITYMATRIX];
yhgt = DEFAULTFONTSIZE;
}
/* get tic font initialized */
[ticfont set];
PSnewpath();
PSsetlinewidth([ticMarkThicknessText floatValue]);
if (xaxislog) { /* x-axis is logarithmic */
nmin = (int)floor((double)(xmin/ppxunit));
nmax = (int)ceil((double)(xmax/ppxunit));
ninc = (int)rint(log10(xinc_unscaled));
if (ninc == 0) ninc = 1; /* avoid infinite loop */
if ([handFormatXaxis state] == 1) {
axformat[0] = [xFormatLeft intValue];
axformat[1] = [xFormatRight intValue];
axformat[2] = [xFormatExponent intValue];
}
for (j=nmin; j<=nmax; j+=ninc) {
if ( (float)j * ppxunit >= xmin && (float)j * ppxunit <= xmax ) {
if (drawGrid) {
PSsetlinewidth([gridThicknessText floatValue]);
PSmoveto((float)j * ppxunit, ymin);
if ([gridDotted state])
PSsetdash(pattern2, 2, 0.0);
else
PSsetdash(pattern0, 0, 0.0);
PSrlineto(0.0, ABS(ymax-ymin));
PSstroke();
PSsetlinewidth([ticMarkThicknessText floatValue]);
}
PSmoveto((float)j * ppxunit, ymin - ticmarklen*6.0); /* big tic mark */
PSsetdash(pattern0, 0, 0.0);
PSrlineto(0.0, ticmarklen*6.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto((float)j * ppxunit, ymax); /* big tic mark */
PSsetdash(pattern0, 0, 0.0);
PSrlineto(0.0, ticmarklen*6.0);
PSstroke();
}
if ([handFormatXaxis state] == 0) {
ticloc_rat = yhgt/DEFAULTFONTSIZE;
PSmoveto((float)j * ppxunit - ticloc_rat*8.0,
ymin - MAX(24.0*ticloc_rat,
24.0*ticloc_rat*(ticmarklen+10.0)/10.0));
PSshow("10");
[ticfont1 set];
PSmoveto((float)j * ppxunit + ticloc_rat*4.0,
ymin - MAX(16.0*ticloc_rat,
8.0*ticloc_rat*(2.0 + 0.3*ticmarklen)));
sprintf(ticlabel, "%-5d", j);
PSshow(ticlabel);
[ticfont set];
}
else {
x = (float) pow((double)10.0, (double)j);
handformat(x, ticlabel, axformat);
xwid = [ticfont getWidthOf:ticlabel];
PSmoveto((float)j * ppxunit - xwid/2.0,
ymin - yhgt - MAX(5.0, 5.0*ticmarklen));
PSshow(ticlabel);
}
}
if (drawMinorTics) {
for (i=2; i<=9; i++) {
ticloc = (float)j * ppxunit + ppxunit*(float)log10((double)i);
if ( ticloc>xmin && ticloc<xmax ) {
PSmoveto(ticloc, ymin - ticmarklen*3.0); /* small tic mark */
PSrlineto(0.0, ticmarklen*3.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto(ticloc, ymax); /* small tic mark */
PSrlineto(0.0, ticmarklen*3.0);
PSstroke();
}
}
}
}
}
}
else { /* x-axis is linear */
yticloc = (ticLocation > 0 ? ymin : 2.0*ticmarklen) ;
/* If inc is big, skip tic marks entirely */
if (fabs(xinc_unscaled) < fabs(xmax_unscaled - xmin_unscaled)) {
count_labels(&nlabels, &first, xmin_unscaled, xinc_unscaled, xmax_unscaled);
if ([handFormatXaxis state] == 1) {
axformat[0] = [xFormatLeft intValue];
axformat[1] = [xFormatRight intValue];
axformat[2] = [xFormatExponent intValue];
}
else {
autoformat(xmin_unscaled, xinc_unscaled, xmax_unscaled, axformat);
[xFormatLeft setIntValue:axformat[0]];
[xFormatRight setIntValue:axformat[1]];
[xFormatExponent setIntValue:axformat[2]];
}
/*
* next loop starts at -1 because there may be room for minor tic
* marks to the left of the first major tic mark (after a zoom, e.g.)
*/
for (i = -1; i < nlabels; i++) {
/* Special test here for what should be exact 0 (but isn't sometimes
* due to floating-point arithmetic.
*/
if (fabs(first/xinc_unscaled + (float)i) < 4.0e-7) { /* ugly */
x = 0.0;
}
else {
x = (first + (xinc_unscaled)*(float)i) * ppxunit;
}
if (x >= xmin) { /* ensure major tic mark won't be off edge */
if (drawGrid) {
PSsetlinewidth([gridThicknessText floatValue]);
PSmoveto(x, ymin);
if ([gridDotted state])
PSsetdash(pattern2, 2, 0.0);
else
PSsetdash(pattern0, 0, 0.0);
PSrlineto(0.0, ABS(ymax-ymin));
PSstroke();
PSsetlinewidth([ticMarkThicknessText floatValue]);
}
/* Nothing at 0 if we're putting tic marks on axes: */
if (ticLocation > 0 || x != 0.0) {
PSmoveto(x, yticloc - ticmarklen*4.0);
PSsetdash(pattern0, 0, 0.0);
PSrlineto(0.0, ticmarklen*4.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto(x, ymax);
PSsetdash(pattern0, 0, 0.0);
PSrlineto(0.0, ticmarklen*4.0);
PSstroke();
}
handformat(x/ppxunit, ticlabel, axformat);
xwid = [ticfont getWidthOf:ticlabel];
PSmoveto(x - xwid/2.0, yticloc - yhgt - MAX(5.0, 5.0*ticmarklen));
PSshow(ticlabel);
}
}
if (drawMinorTics) {
if (ticLocation > 0) { /* tic marks on frame */
for (j=1; j<=9; j++) {
ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit;
if (ticloc>xmin && ticloc<xmax) {
PSmoveto(ticloc, yticloc - ticmarklen*2.0);
PSrlineto(0.0, ticmarklen*2.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto(ticloc, ymax);
PSrlineto(0.0, ticmarklen*2.0);
PSstroke();
}
}
}
}
else {
for (j=1; j<=9; j++) {
ticloc = x + ((xinc_unscaled/10.0)*(float)j)*ppxunit;
if (ticloc>xmin && ticloc<xmax) {
PSmoveto(ticloc, -ticmarklen);
PSrlineto(0.0, 2.0*ticmarklen);
PSstroke();
}
}
}
}
}
}
}
if (yaxislog) { /* y-axis is logarithmic */
nmin = (int)floor((double)(ymin/ppyunit));
nmax = (int)ceil((double)(ymax/ppyunit));
ninc = (int)rint(log10(yinc_unscaled));
if (ninc == 0) ninc = 1; /* avoid infinite loop */
if ([handFormatYaxis state] == 1) {
axformat[0] = [yFormatLeft intValue];
axformat[1] = [yFormatRight intValue];
axformat[2] = [yFormatExponent intValue];
}
for (j=nmin; j<=nmax; j+=ninc) {
if ( (float)j * ppyunit >= ymin && (float)j * ppyunit <= ymax ) {
if (drawGrid) {
PSsetlinewidth([gridThicknessText floatValue]);
PSmoveto(xmin, (float)j * ppyunit);
if ([gridDotted state])
PSsetdash(pattern2, 2, 0.0);
else
PSsetdash(pattern0, 0, 0.0);
PSrlineto(ABS(xmax-xmin), 0.0);
PSstroke();
PSsetlinewidth([ticMarkThicknessText floatValue]);
}
PSmoveto(xmin - ticmarklen*6.0, (float)j * ppyunit); /* big tic mark */
PSsetdash(pattern0, 0, 0.0);
PSrlineto(ticmarklen*6.0, 0.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto(xmax, (float)j * ppyunit); /* big tic mark */
PSsetdash(pattern0, 0, 0.0);
PSrlineto(ticmarklen*6.0, 0.0);
PSstroke();
}
if ([handFormatYaxis state] == 0) {
ticloc_rat = yhgt/DEFAULTFONTSIZE;
PSmoveto(xmin - MAX(40.0*ticloc_rat,
40.0*ticloc_rat*(ticmarklen+10.0)/10.0),
(float)j * ppyunit - ticloc_rat*7.0);
PSshow("10");
[ticfont1 set];
PSmoveto(xmin - MAX(24.0*ticloc_rat,
4.0*ticloc_rat*(ticmarklen + 6.0)),
(float)j * ppyunit - ticloc_rat*1.0);
sprintf(ticlabel, "%-5d", j);
PSshow(ticlabel);
[ticfont set];
}
else {
y = (float) pow((double)10.0, (double)j);
handformat(y, ticlabel, axformat);
xwid = [ticfont getWidthOf:ticlabel];
PSmoveto(xmin - xwid - MAX(10.0, 5.0*ticmarklen),
(float)j * ppyunit + 2.0 - yhgt/2.0);
PSshow(ticlabel);
}
}
if (drawMinorTics) {
for (i=2; i<=9; i++) {
ticloc = (float)j * ppyunit + ppyunit*(float)log10((double)i);
if (ticloc>ymin && ticloc<ymax) {
PSmoveto(xmin - ticmarklen*3.0, ticloc); /* small tic mark */
PSrlineto(ticmarklen*3.0, 0.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto(xmax, ticloc); /* small tic mark */
PSrlineto(ticmarklen*3.0, 0.0);
PSstroke();
}
}
}
}
}
}
else { /* y-axis is linear */
xticloc = (ticLocation > 0 ? xmin : 2.0*ticmarklen) ;
/* If inc is big, skip tic marks entirely */
if (fabs(yinc_unscaled) < fabs(ymax_unscaled - ymin_unscaled)) {
count_labels(&nlabels, &first, ymin_unscaled, yinc_unscaled, ymax_unscaled);
if ([handFormatYaxis state] == 1) {
axformat[0] = [yFormatLeft intValue];
axformat[1] = [yFormatRight intValue];
axformat[2] = [yFormatExponent intValue];
}
else {
autoformat(ymin_unscaled, yinc_unscaled, ymax_unscaled, axformat);
[yFormatLeft setIntValue:axformat[0]];
[yFormatRight setIntValue:axformat[1]];
[yFormatExponent setIntValue:axformat[2]];
}
/*
* next loop starts at -1 because there may be room for minor tic
* marks to the left of the first major tic mark (after a zoom, e.g.)
*/
for (i = -1; i < nlabels; i++) {
/* Special test here for what should be exact 0 (but isn't sometimes
* due to floating-point arithmetic.
*/
if (fabs(first/yinc_unscaled + (float)i) < 4.0e-7) { /* ugly */
y = 0.0;
}
else {
y = (first + (yinc_unscaled)*(float)i) * ppyunit;
}
if (y >= ymin) { /* ensure major tic mark won't be off edge */
if (drawGrid) {
PSsetlinewidth([gridThicknessText floatValue]);
PSmoveto(xmin, y);
if ([gridDotted state])
PSsetdash(pattern2, 2, 0.0);
else
PSsetdash(pattern0, 0, 0.0);
PSrlineto(ABS(xmax-xmin), 0.0);
PSstroke();
PSsetlinewidth([ticMarkThicknessText floatValue]);
}
/* Nothing at 0 if we're putting tic marks on axes: */
if (ticLocation > 0 || y != 0.0) {
PSmoveto(xticloc - ticmarklen*4.0, y);
PSsetdash(pattern0, 0, 0.0);
PSrlineto(ticmarklen*4.0, 0.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto(xmax, y);
PSsetdash(pattern0, 0, 0.0);
PSrlineto(ticmarklen*4.0, 0.0);
PSstroke();
}
handformat(y/ppyunit, ticlabel, axformat);
xwid = [ticfont getWidthOf:ticlabel];
PSmoveto(xticloc - xwid - MAX(10.0, 5.0*ticmarklen),
y + 2.0 - yhgt/2.0);
PSshow(ticlabel);
}
}
if (drawMinorTics) {
if (ticLocation > 0) { /* tics on frame */
for (j=1; j<=9; j++) {
ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit;
if (ticloc>ymin && ticloc<ymax) {
PSmoveto(xticloc - ticmarklen*2.0, ticloc);
PSrlineto(ticmarklen*2.0, 0.0);
PSstroke();
if (ticLocation == 2) { /* tics on right and top */
PSmoveto(xmax, ticloc);
PSrlineto(ticmarklen*2.0, 0.0);
PSstroke();
}
}
}
}
else {
for (j=1; j<=9; j++) {
ticloc = y + ((yinc_unscaled/10.0)*(float)j)*ppyunit;
if (ticloc>ymin && ticloc<ymax) {
PSmoveto(-ticmarklen, ticloc);
PSrlineto(2.0*ticmarklen, 0.0);
PSstroke();
}
}
}
}
}
}
}
return self;
}
- drawSelf: (const NXRect *)rects :(int)rectCount
{
float xmin = (float)[plotParam provideXmin];
float xmax = (float)[plotParam provideXmax];
float ymin = (float)[plotParam provideYmin];
float ymax = (float)[plotParam provideYmax];
[self startPlot];
if ([plotParam nFiles] == 0) return self; /* no data */
if (xmin==xmax || ymin==ymax) return self; /* avoid division by zero */
PSgsave();
PSsetmiterlimit(1); // suggested by Carrick Talmadge
// (clt@eotvos.physics.purdue.edu)
PStranslate(XOFFSET, YOFFSET);
if ([plotParam xaxisLog]) { /* x-axis is logarithmic */
ppxunit = 0.9*(bounds.size.width-XOFFSET)
/(float)log10((double)(xmax/xmin));
xmin = (float)log10((double)xmin) * ppxunit;
xmax = (float)log10((double)xmax) * ppxunit;
}
else { /* x-axis is linear */
ppxunit = 0.9*(bounds.size.width-XOFFSET)/(xmax-xmin);
xmin = xmin*ppxunit; /* drawing is all in pixel coordinates */
xmax = xmax*ppxunit;
}
if ([plotParam yaxisLog]) { /* y-axis is logarithmic */
ppyunit = 0.9*(bounds.size.height-YOFFSET)
/(float)log10((double)(ymax/ymin));
ymin = (float)log10((double)ymin) * ppyunit;
ymax = (float)log10((double)ymax) * ppyunit;
}
else { /* y-axis is linear */
ppyunit = 0.9*(bounds.size.height-YOFFSET)/(ymax-ymin);
ymin = ymin*ppyunit;
ymax = ymax*ppyunit;
}
PStranslate(-xmin, -ymin);
// inner "frame" box
if ([frameBoxOnOff state]) {
PSsetlinewidth([frameBoxThicknessText floatValue]);
PSnewpath();
PSmoveto(xmin, ymin);
PSlineto(xmax, ymin);
PSlineto(xmax, ymax);
PSlineto(xmin, ymax);
PSclosepath();
PSstroke();
/* reset line width */
PSsetlinewidth(NXDrawingStatus==NX_DRAWING? 0.0 :
[accPrintColorButton state]==0? LINE_WIDTH_IF_PRINTING_BW :
LINE_WIDTH_IF_PRINTING_COLOR);
}
if ([axesOnOff state]) {
PSnewpath();
PSsetlinewidth([axisThicknessText floatValue]);
PSmoveto(0.0, ymin);
PSlineto(0.0, ymax);
PSstroke();
PSmoveto(xmin, 0.0);
PSlineto(xmax, 0.0);
PSstroke();
}
if ([majorTicMarksOnOff state])
[self drawTicMarks:xmin :xmax :ymin :ymax]; /* do this before clipping */
/* MIN and MAX below in case xmin > xmax and/or ymin > ymax. */
PSrectclip(MIN(xmin,xmax), MIN(ymin,ymax), ABS(xmax-xmin), ABS(ymax-ymin));
[self drawLines:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]];
[self drawSymbols:plotParam :[plotParam xaxisLog] :[plotParam yaxisLog]];
[self drawErrorBars :[plotParam xaxisLog] :[plotParam yaxisLog]];
PSgrestore();
if ([legendOnOff state]) {
[self startLegend];
[self drawLegend:self];
}
return self;
}
- doPrinting:sender
{
id myPrintPanel = [PrintPanel new];
[ [NXApp printInfo] setMarginLeft:72.0 right:72.0 top:72.0 bottom:72.0 ];
if (bounds.size.height > bounds.size.width) { /* portrait mode */
if (bounds.size.height/bounds.size.width > 9.0/6.5) {
[ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.height];
}
else {
[ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.width];
}
[ [NXApp printInfo] setOrientation:NX_PORTRAIT andAdjust:YES ];
}
else { /* landscape mode */
if (bounds.size.width/bounds.size.height > 9.0/6.5) {
[ [NXApp printInfo] setScalingFactor: 9.0*72.0/bounds.size.width];
}
else {
[ [NXApp printInfo] setScalingFactor: 6.5*72.0/bounds.size.height];
}
[ [NXApp printInfo] setOrientation:NX_LANDSCAPE andAdjust:YES ];
}
[myPrintPanel setAccessoryView:printPanelAccessory];
[self printPSCode:sender];
return self;
}
- mouseDown:(NXEvent *)e
/*
* This code taken from the Mandelbrot example in /NextDeveloper (and modified).
* We implement the mouseDown method so the user can sweep out a section of
* the view and select that as the current window in which to view the curve(s).
*/
{
int looping = YES;
int oldMask;
NXRect bbox;
NXPoint startPoint, currPoint;
float xmin, xmax, ymin, ymax;
register float t1, t2, t3, t4;
int zooming;
BOOL xaxislog = [plotParam xaxisLog];
BOOL yaxislog = [plotParam yaxisLog];
zooming = ZOOM;
if (strncmp([zoomChoice title], "Zoom/Move", 9) == 0)
zooming = NOZOOM;
else if (strncmp([zoomChoice title], "Move legend", 11) == 0)
zooming = MOVELEGEND;
else if (strncmp([zoomChoice title], "Move x title", 12) == 0)
zooming = MOVEXTITLE;
else if (strncmp([zoomChoice title], "Move y title", 12) == 0)
zooming = MOVEYTITLE;
else if (strncmp([zoomChoice title], "Move main title", 15) == 0)
zooming = MOVEMAINTITLE;
if (zooming == ZOOM) { /* really zooming */
if (xaxislog) {
xmin = (float)log10([plotParam provideXmin]) * ppxunit;
xmax = (float)log10([plotParam provideXmax]) * ppxunit;
}
else {
xmin = [plotParam provideXmin] * ppxunit;
xmax = [plotParam provideXmax] * ppxunit;
}
if (yaxislog) {
ymin = (float)log10([plotParam provideYmin]) * ppyunit;
ymax = (float)log10([plotParam provideYmax]) * ppyunit;
}
else {
ymin = [plotParam provideYmin] * ppyunit;
ymax = [plotParam provideYmax] * ppyunit;
}
oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
startPoint = e->location;
[self convertPoint:&startPoint fromView:nil];
NXSetRect(&bbox,startPoint.x,startPoint.y,0.0,0.0);
[self lockFocus];
while (looping) {
e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
currPoint = e->location;
[self convertPoint:&currPoint fromView:nil];
bbox.size.width = (currPoint.x - startPoint.x);
bbox.size.height = (currPoint.y - startPoint.y);
/* Normalize bbox to always have positive width and height */
if (bbox.size.width < 0) {
bbox.size.width = -bbox.size.width;
bbox.origin.x = startPoint.x - bbox.size.width;
}
if (bbox.size.height < 0) {
bbox.size.height = -bbox.size.height;
bbox.origin.y = startPoint.y - bbox.size.height;
}
PSnewinstance();
if (looping = (e->type == NX_MOUSEDRAGGED)) {
PSsetinstance(YES);
NXSetColor([plotParam provideTextColor]);
NXFrameRect(&bbox);
PSsetinstance(NO);
}
}
[self unlockFocus];
[window setEventMask:oldMask];
if ((bbox.size.width > 0) && (bbox.size.height > 0)) {
/* At this point, bbox is in window coordinates. Convert to curve
* coordinates based on this view's coordinates and the bounding box.
*/
xmin = xmin + (bbox.origin.x - XOFFSET);
xmax = xmin + bbox.size.width;
ymin = ymin + (bbox.origin.y - YOFFSET);
ymax = ymin + bbox.size.height;
/* save old min/max -- must adjust if log axis */
t1 = xmin/ppxunit;
t2 = xmax/ppxunit;
t3 = ymin/ppyunit;
t4 = ymax/ppyunit;
if (xaxislog) {
t1 = (float) pow((double)10.0, (double)t1);
t2 = (float) pow((double)10.0, (double)t2);
}
if (yaxislog) {
t3 = (float) pow((double)10.0, (double)t3);
t4 = (float) pow((double)10.0, (double)t4);
}
[plotParam stackOldMinMax:t1 :t2 :t3 :t4];
if (xaxislog) {
[plotParam resetXmin:pow((double)10.0, (double)(xmin/ppxunit))];
[plotParam resetXmax:pow((double)10.0, (double)(xmax/ppxunit))];
}
else {
[plotParam resetXmin:(double)(xmin/ppxunit)];
[plotParam resetXmax:(double)(xmax/ppxunit)];
}
if (yaxislog) {
[plotParam resetYmin:pow((double)10.0, (double)(ymin/ppyunit))];
[plotParam resetYmax:pow((double)10.0, (double)(ymax/ppyunit))];
}
else {
[plotParam resetYmin:(double)(ymin/ppyunit)];
[plotParam resetYmax:(double)(ymax/ppyunit)];
}
/* Call [self display] to force current values of xmin/xmax/ymin/ymax
* to take effect (otherwise xmin, etc., get incremented too many times).
*/
[plotParam drawPlotButton:1];
[self display];
[plotParam drawPlotButton:0];
}
}
else if (zooming >= MOVELEGEND) {
oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
startPoint = e->location;
[self convertPoint:&startPoint fromView:nil];
if (zooming == MOVELEGEND) {
NXSetRect(&bbox,startPoint.x,startPoint.y,
legendbox.size.width,legendbox.size.height);
}
else if (zooming == MOVEXTITLE) {
NXSetRect(&bbox,startPoint.x,startPoint.y,
xtitlebox.size.width,xtitlebox.size.height);
}
else if (zooming == MOVEYTITLE) {
NXSetRect(&bbox,startPoint.x,startPoint.y,
ytitlebox.size.width,ytitlebox.size.height);
}
else if (zooming == MOVEMAINTITLE) {
NXSetRect(&bbox,startPoint.x,startPoint.y,
maintitlebox.size.width,maintitlebox.size.height);
}
[self lockFocus];
while (looping) {
e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
currPoint = e->location;
[self convertPoint:&currPoint fromView:nil];
bbox.origin.x = currPoint.x;
bbox.origin.y = currPoint.y;
PSnewinstance();
if (looping = (e->type == NX_MOUSEDRAGGED)) {
PSsetinstance(YES);
NXSetColor([plotParam provideTextColor]);
NXFrameRect(&bbox);
PSsetinstance(NO);
}
}
[self unlockFocus];
[window setEventMask:oldMask];
/*
* At this point, bbox is in window coordinates.
*/
if (zooming == MOVELEGEND) {
legendbox.origin.x = bbox.origin.x;
legendbox.origin.y = bbox.origin.y;
}
else if (zooming == MOVEXTITLE) {
xtitlebox.origin.x = bbox.origin.x;
xtitlebox.origin.y = bbox.origin.y;
}
else if (zooming == MOVEYTITLE) {
ytitlebox.origin.x = bbox.origin.x;
ytitlebox.origin.y = bbox.origin.y;
}
else if (zooming == MOVEMAINTITLE) {
maintitlebox.origin.x = bbox.origin.x;
maintitlebox.origin.y = bbox.origin.y;
}
/* Call [self display] to give immediate feedback */
[plotParam drawPlotButton:1];
[self display];
[plotParam drawPlotButton:0];
}
return self;
}
- saveEPS:sender
{
id savePanel = [[SavePanel new] setRequiredFileType:"eps"];
char *eps_fileName; // Name of the EPS file for output
[savePanel setTitle:"Save EPS"]; /* make sure title is OK */
if ([savePanel runModal]) {
eps_fileName = (char *) calloc(strlen([savePanel filename])+1, sizeof(char));
strcpy(eps_fileName, [savePanel filename]);
[self savePSCode:eps_fileName];
}
return self;
}
- savePSCode:(char *)aFile
{
NXStream *psStream;
psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
if (!psStream) {
return self;
}
[self getBounds:&bounds];
[self copyPSCodeInside:&bounds to:psStream];
NXFlush(psStream);
NXSaveToFile(psStream, aFile);
NXCloseMemory(psStream, NX_FREEBUFFER);
return self;
}
/*
* Following code taken from the Graph example in NextDeveloper/Examples.
*
* Copies the current view into the Pasteboard as PostScript.
*/
- copyPScode:sender
{
NXStream *psStream;
id pb;
char *data;
int dataLen, maxDataLen;
/* Open a stream on memory where we will collect the PostScript */
psStream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
if (!psStream)
return self;
/* Tell the Pasteboard we're going to copy PostScript */
pb = [Pasteboard new];
[pb declareTypes:&NXPostScriptPboardType num:1 owner:self];
/* writes the PostScript for the whole plot as EPS into the stream */
[self getBounds:&bounds];
[self copyPSCodeInside:&bounds to:psStream];
/* get the buffered up PostScript out of the stream */
NXGetMemoryBuffer(psStream, &data, &dataLen, &maxDataLen);
/* put the buffer in the Pasteboard, free the stream (and the buffer) */
[pb writeType:NXPostScriptPboardType data:data length:dataLen];
NXCloseMemory(psStream, NX_FREEBUFFER);
return self;
}
@end